Skip to content

feat: implement EDA long-term memory plugin and upgrade plugin system infrastructure#1870

Open
xiaobin83 wants to merge 21 commits into
moeru-ai:mainfrom
xiaobin83:feat/openviking-memory-plugin
Open

feat: implement EDA long-term memory plugin and upgrade plugin system infrastructure#1870
xiaobin83 wants to merge 21 commits into
moeru-ai:mainfrom
xiaobin83:feat/openviking-memory-plugin

Conversation

@xiaobin83
Copy link
Copy Markdown

@xiaobin83 xiaobin83 commented May 24, 2026

Description

This PR adds the openviking-memory EDA (Event-Driven Architecture) long-term memory plugin, and supplements & optimizes the project-wide plugin system infrastructure.

Core changes are as follows:

  • stage-tamagotchi: Implemented PluginHost config system with schema validation, Eventa IPC, settings UI and i18n; redesigned plugin config UX with dirty tracking; fixed plugin tool timing and config application bugs.

  • plugin-sdk: Extended plugin-tools with memory context injection and post-processing hooks.

  • stage-ui: Registered plugin context provider for chat orchestrator to support plugin capability docking.

  • openviking-memory: Implemented complete memory CRUD capabilities based on PluginHost SDK, sorted out plugin code structure, and added supporting build pipeline.

Additional Context

Please focus on the main project code modifications (stage-tamagotchi / plugin-sdk / stage-ui) during review, including plugin host configuration logic, SDK hook extension, chat orchestrator context registration and related bug fixes, to verify main process stability and capability compatibility.

Known Issues

  • The is only Chinese README.zh-CN.md in openviking-memory folder.

Coming soon

  • Character isolated memory
  • Account isolated memory
  • A proper plugin installer (by drag-n-drop)

Screenshots

Notice, the first two screenshots were captured from development mode. The last one was captured from app.

Open a new chat,
截屏2026-05-24 18 59 36

Reset the chat,
截屏2026-05-24 19 00 10

Restart the app,
截屏2026-05-24 19 30 19

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1215d52ef2

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/stage-tamagotchi/src/main/services/airi/plugins/host/index.ts Outdated
Comment thread apps/stage-tamagotchi/src/renderer/stores/plugin-tools.ts Outdated
Comment thread apps/stage-tamagotchi/src/renderer/stores/plugin-tools.ts Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f554c1bdbe

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/stage-tamagotchi/src/main/services/airi/plugins/host/config.ts Outdated
Comment thread apps/stage-tamagotchi/src/main/services/airi/plugins/host/index.ts
@xiaobin83
Copy link
Copy Markdown
Author

xiaobin83 commented May 24, 2026

update:
the implementation has been changed to use runtimeContextProviders.


I noticed there is runtimeContextProviders added in chat-orchestrator-runtime.ts 5 days ago, which can be used for my injectMemoryContext, I'll change the implementation. I was using onBeforeCompose (added just in the place of runtimeContextProviders).

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d67784f9fb

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/stage-tamagotchi/src/renderer/stores/plugin-tools.ts Outdated
Comment thread apps/stage-tamagotchi/src/renderer/stores/plugin-tools.ts Outdated
Comment thread apps/stage-tamagotchi/src/renderer/stores/plugin-tools.ts Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2c59f1d806

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/stage-tamagotchi/src/main/services/airi/plugins/host/index.ts Outdated
Comment thread apps/stage-tamagotchi/src/main/services/airi/plugins/host/index.ts Outdated
@xiaobin83 xiaobin83 force-pushed the feat/openviking-memory-plugin branch from 2c59f1d to 85dfcbf Compare May 25, 2026 15:53
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 85dfcbf5a0

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/stage-tamagotchi/src/renderer/stores/plugin-tools.ts
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ce5e8a78cd

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/stage-tamagotchi/src/renderer/stores/plugin-tools.ts Outdated
Comment thread apps/stage-tamagotchi/src/main/services/airi/plugins/host/index.ts
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 13a96c1d8b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/stage-tamagotchi/src/renderer/stores/plugin-tools.ts Outdated
@xiaobin83 xiaobin83 force-pushed the feat/openviking-memory-plugin branch from e367d49 to f046694 Compare May 25, 2026 18:40
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f046694358

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/stage-tamagotchi/src/main/services/airi/plugins/host/index.ts
@xiaobin83 xiaobin83 force-pushed the feat/openviking-memory-plugin branch from 85ed762 to d2b515e Compare May 26, 2026 15:25
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d2b515e307

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/stage-tamagotchi/src/renderer/stores/plugin-tools.ts Outdated
Comment thread apps/stage-tamagotchi/src/main/services/airi/plugins/host/index.ts Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 93d7f73985

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/stage-tamagotchi/src/renderer/pages/settings/plugins/index.vue Outdated
Comment thread apps/stage-tamagotchi/src/renderer/pages/settings/plugins/index.vue Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 34c9e9ae56

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/stage-ui/src/stores/chat/session-store.ts Outdated
xiaobin83 and others added 15 commits May 28, 2026 01:09
…onfig system, context hooks, and example plugin

- stage-tamagotchi: add PluginHost config system with schema validation, Eventa IPC, settings UI, and i18n

- plugin-sdk: extend plugin-tools with injectMemoryContext and post-processing hooks

- stage-ui: add plugin context provider registration for chat orchestrator

- stage-tamagotchi: redesign plugin settings config UX with dirty tracking

- stage-tamagotchi: fix plugin tool timing and config apply workaround

- openviking-memory: add example plugin with memory CRUD via PluginHost SDK, reorganize source, and add build pipeline

- docs: update plugin host design documents
…ptions

Add config?: ModuleConfigEnvelope to PluginStartOptions for passing updated config to init

Fix PluginHost.init() to use options.config when calling applyConfiguration, instead of always passing an empty envelope

Update loadPluginByName in stage-tamagotchi host to accept and forward config to host.start

Let loadEnabledPlugins build a config envelope from the already-fetched config and pass it directly, avoiding a redundant getConfig() call

Let setPluginConfig pass the updated config envelope via host.reload() options so reloaded sessions receive the latest config
Reset conversationSessionId via useChatMaintenanceStore $onAction when cleanupMessages fires, so plugin memory saves start a fresh session.

- Fix save conversation not working across cleanup boundaries - Reimplement injectMemoryContext as runtime context provider
…rom chat session

- Wrap listPlugins() calls in injectMemoryContext and onChatTurnComplete
  with try-catch to soft-fail on transient errors
- Derive conversationSessionId from active chat session instead of a
  global ref; remove cleanupMessages workaround
- Remove noisy console.info logs from plugin-tools and openviking
…te on plugin config reload

Revoke prior asset sessions after host.reload triggered by setPluginConfig, preventing stale 30-day TTL asset URLs/cookies from accumulating.

Clear loaded state (loaded set + loadedSessionIds) and revoke asset sessions when host.reload throws, preventing inconsistent host state where a plugin appears loaded but holds a stale session id.

- clearModuleAssetSessionCacheByOwnerSessionId + revokeByOwnerSessionId called after successful host.reload for the old session

- On reload failure, loaded/loadedSessionIds are cleaned up and old asset sessions revoked before re-throwing the error
…rt it

Filter `memory_save_conversation` invocation to only plugins that actually register the tool, eliminating warning noise and unnecessary IPC work on every chat turn.

- Extract 'memory_save_conversation' string into MEMORY_SAVE_CONVERSATION_TOOL constant

- Cache plugin IDs that expose this tool during refresh() from xsai tool definitions

- Filter loaded+enabled plugins in registerPostProcessingHook against the cache

- Clear the cache in dispose()
…ect memory_search tool invocation

Remove the intermediate queryContext Eventa IPC pathway and the associated contract definition file

Replace indirect queryContext calls with direct invokePluginTool calls to the memory_search tool in the renderer store

Add limit parameter support to searchMemories API with default value

Split pluginsWithMemoryTool into separate pluginsWithMemorySaveTool and pluginsWithMemorySearchTool sets for clearer responsibility

Clean up stale mocks and imports in tests
…posing sends

The chat send path blocked on plugin context lookup because performSend awaits runtime context providers, and injectMemoryContext invoked invokePluginTool sequentially for each loaded plugin. When the OpenViking backend is down, memory_search could take ~30s+ per plugin due to retry+timeout, freezing user sends before any token streams.

Changes: - Replace sequential for-loop with concurrent Promise.allSettled - Add AbortSignal.timeout(3_000) per plugin invoke to fail fast - Each slow/timed-out plugin is silently skipped with a warning - Maximum total wait is bounded to 3s regardless of plugin count
- Add required field validation: reject missing or empty required fields
- Add value type validation: enforce string/secret/number/boolean per schema
- Preserve existing unknown key rejection with stricter type assertion
- Add comprehensive unit test suite (13 test cases covering all validation paths)
…d cleanup in setPluginConfig

- Split single try/catch into two: one for host.reload() failures,
  another for post-reload asset cookie revocation
- Log cleanup errors instead of clearing host bookkeeping when
  reload succeeded but asset revocation fails
- Prevents host bookkeeping inconsistency where a running session is
  marked as unloaded due to non-fatal cleanup errors
…atTurnComplete to prevent misattribution

- Change ensureConversationSessionId signature to require (sessionId, generation) parameters so callers must provide captured metadata
- Move sessionId/generation capture before the await listPlugins() call in onChatTurnComplete callback to lock the originating session synchronously
- This ensures that a concurrent session switch during streaming does not persist the completed turn under the wrong session ID in long-term memory
…ation

Replace generic Error with TypeError in host plugin config validation for more precise error semantics when type mismatches occur.

- Config field string type validation

- Config field number type validation

- Config field boolean type validation
… in plugin config validation

The required-field guard in setPluginConfig only rejected empty strings for type === 'string', but skipped the same check for type === 'secret'. This allowed a schema field marked { required: true, type: 'secret' } to be saved as '', persisted, and reloaded as an unusable credential.

- Extended the empty-value check to cover both 'string' and 'secret' types

- Made the error message dynamic to reflect the actual field type
Eliminate a race condition in plugin-tools where a concurrent session switch during streaming could misattribute a completed turn to the wrong session. The sessionId and generation are now carried inside ChatStreamEventContext instead of read from the session store at async handler time.

- Add sessionId and generation fields to ChatStreamEventContext so downstream consumers identify the originating session without external store state

- Persist generation counter in ChatSessionMeta so it survives reloads; ensureGeneration and loadIndexForUser restore from persisted value

- Remove useChatSessionStore dependency from plugin-tools store; read sessionId and generation from the event context instead
…er inputs in plugin settings

Preserve unsaved config edits when plugin list refreshes instead of resetting all editors; merge existing editor states and only add/remove entries for new/stale plugins

Reject invalid number input in saveConfig with an inline error message instead of silently omitting the value from the saved config

Reload only the saved plugin's config after savePluginConfig instead of reloading all configs, preventing the refresh from discarding unsaved edits in other plugin forms

Remove now-unused loadAllConfigs helper
xiaobin83 added 6 commits May 28, 2026 01:09
applyRemoteSnapshot previously kept the local generation when already initialized, causing follower windows to retain stale generation values after another window resets a chat. Since memory session IDs include the generation, subsequent turns in the follower would be persisted under the pre-reset namespace, mixing old and new memory history.

- Remove fallback to sessionGenerations.value[sessionId] when applying remote snapshot

- Always trust snapshot.sessionMetas[sessionId]?.generation for each session in the incoming snapshot
The refactor-remote-plugins-to-plugin-host planning document has served its purpose — M1-M3 are completed and the core migration is done. Removing it to keep the .trae/documents directory clean.
…commit pattern

Redesign saveConversation to follow OpenViking's add-accumulate-commit pattern instead of blind message posting.

- Add SessionInfo interface and addSessionMessage/getSession/commitSession methods

- Add commitTokenThreshold (20k) and commitKeepRecentCount (10) config options

- Add committed flag to ConversationSaveResult

- Fix getSession to correctly parse { status, result, error } response
…urn, translate docs to English

Add recallMemories method to OpenVikingClient (delegates to searchMemories)

Register memory_recall tool for system-triggered context injection

Rename memory_save_conversation tool to memory_save_turn

Rename saveConversation to saveTurn in client interface and implementation

Translate all tool titles, descriptions, and parameter docs from Chinese to English

Mark system-only tools (memory_recall, memory_save_turn) with empty activation keywords

Update plugin-tools.ts constants to reference memory_recall and memory_save_turn

Update Readme with memory_recall, memory_save_turn, session memory processing rules, and system-triggered tool usage notes
…rategy

Replace simple recallMemories delegation with a full sorting pipeline for context injection: query intent analysis, multi-boost scoring, dedup, and leaf-first selection.

- Add ranking.ts module with FindResultItem/RecallQueryProfile types

- Implement buildRecallQueryProfile for token extraction and intent detection

- Implement rankForInjection with leafBoost, eventBoost, preferenceBoost, and lexicalOverlapBoost scoring components

- Implement pickMemoriesForInjection entry: rank to sort to dedup to leaf-first

- Update recallMemories to fetch 3x candidates and apply ranking pipeline

- Add recallCandidateMultiplier property to OpenVikingClient interface
…on analysis

Remove the 553-line `openclaw-plugin-implementation-analysis.md` document.

This analysis is being superseded by the new rank-for-injection design documents.
@xiaobin83 xiaobin83 force-pushed the feat/openviking-memory-plugin branch from 162f5eb to 5ba6a9e Compare May 27, 2026 17:09
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5ba6a9e5be

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +168 to +169
if (gathered.length === 0)
return undefined
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Clear stale memory context on empty recall

When a previous send injected memory, the context registry keeps the plugin-memory bucket until another ReplaceSelf message arrives. On later sends where recall returns no results, returning undefined here means ingestRuntimeContexts does not replace or clear that bucket, so formatContextPromptText will still append stale memories to the next prompt. Return an empty/clearing context or otherwise remove the bucket when no memory is available.

Useful? React with 👍 / 👎.

Comment on lines +150 to +151
const contextText = (data.results ?? [])
.map(item => `[${item.uri}]\n${String(item.abstract ?? '')}`)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve overview text in injected memory context

When OpenViking returns results with overview but no abstract (the new FindResultItem type and ranking logic both support this fallback), the injected prompt contains only the URI with an empty body. In that scenario memory recall technically succeeds but gives the model no useful recalled content; use the same abstract ?? overview fallback used by ranking.

Useful? React with 👍 / 👎.

Comment on lines +643 to +647
pluginConfig.update({
...currentConfig,
configs: {
...currentConfig.configs,
[payload.pluginName]: payload.config as Record<string, string | number | boolean>,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Roll back config when live reload fails

For a currently loaded plugin, this persists the new config before host.reload(...) proves the plugin can start with it. If reload throws after schema validation (for example a plugin rejects the endpoint/key during init), setPluginConfig rejects and marks the plugin unloaded, but the bad config is already saved and will be applied again on restart or manual load. Persist after a successful reload, or restore the previous config in the reload failure path.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants